package com.yeyks.commonReference.append.activity.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.common.collect.Lists;
import com.yeyks.common.ajax.AjaxResponseBody;
import com.yeyks.common.constants.activity.RedisKeyPreConstant;
import com.yeyks.common.constants.activity.ZhouJieLun.ZhouJieLunStatisticConstant;
import com.yeyks.common.em.consumer.UserInviteRelationTypeEnum;
import com.yeyks.common.exception.ServiceException;
import com.yeyks.common.result.user.UserDataStatInviteSortResult;
import com.yeyks.common.utils.ArgumentsCheckUtil;
import com.yeyks.common.utils.RedisUtil;
import com.yeyks.common.utils.TextValidator;
import com.yeyks.common.utils.base.PhoneUtil;
import com.yeyks.commonReference.append.activity.ActivityZhouJieLunService;
import com.yeyks.commonReference.append.sms.SmsService;
import com.yeyks.commonReference.append.statistic.StatisticBaseService;
import com.yeyks.commonReference.pojo.param.activity.ZhouJieLun.ActivityZhouJieLunLeaderboardDetailsParam;
import com.yeyks.commonReference.pojo.param.activity.ZhouJieLun.ActivityZhouJieLunLotteryParam;
import com.yeyks.commonReference.pojo.param.activity.ZhouJieLun.ActivityZhouJieLunStatisticIncrementParam;
import com.yeyks.commonReference.pojo.vo.activity.ZhouJieLun.ActivityZhouJieLunLeaderboardDetailsVo;
import com.yeyks.commonReference.pojo.vo.activity.ZhouJieLun.ActivityZhouJieLunLeaderboardDetailsVoInner;
import com.yeyks.commonReference.pojo.vo.activity.ZhouJieLun.ActivityZhouJieLunLotteryVo;
import com.yeyks.commonReference.service.consumer.UserCouponInfoService;
import com.yeyks.commonReference.service.other.CouponInfoService;
import com.yeyks.consumer.dal.domain.UserCouponInfo;
import com.yeyks.consumer.dal.domain.UserDataStat;
import com.yeyks.consumer.dal.domain.UserInfo;
import com.yeyks.consumer.param.UserInfo.UserInfoPhoneLoginParam;
import com.yeyks.consumer.service.UserInfoService;
import com.yeyks.other.dal.domain.ActivityDrawRecord;
import com.yeyks.other.dal.domain.CouponInfo;
import com.yeyks.other.service.ActivityDrawRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/*
活动基础类
 */
@Service
@Slf4j
public class ActivityZhouJieLunServiceImpl implements ActivityZhouJieLunService {

    private Date zhouJieLunActivityEndTime = new Date(2019 - 1900, 10, 14, 12, 0, 0);

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private StatisticBaseService statisticBaseService;
    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private SmsService smsService;
    @Autowired
    private ActivityDrawRecordService activityDrawRecordService;
    @Autowired
    private CouponInfoService couponInfoService;
    @Autowired
    private UserCouponInfoService userCouponInfoService;

    /*
    统计活动
    key:
    活动转发人数  Number of active forwarders  NOAF
    新用户数  Number of new users  NONU
    站内页面访问量  In-site page visits  IPV
    活动页面访问量  Active page visits  APV
     */
    @Override
    public boolean statisticIncrement(ActivityZhouJieLunStatisticIncrementParam param) {
        return statisticBaseService.statisticIncrement(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY, RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_COUNT, param.getKey());
    }

    //抢票,即注册
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(UserInfoPhoneLoginParam param) {
        //判断活动是否结束
        checkActivityEndDate();
        String phone = param.getPhone();
        //判断用户是否存在
        checkUserExist(phone);
        //邀请人
        UserInfo invitUserInfo = userInfoService.getById(param.getUserId());
        if (Objects.isNull(invitUserInfo)) {
            throw new ServiceException("邀请人不存在");
        }
        //设置来源类型
        param.setType(UserInviteRelationTypeEnum.ACTIVITY_ZHOU_JIE_LUN.getValue());
        Boolean result = userInfoService.phoneLogin(param);
        if (result) {
            //新用户统计
            ActivityZhouJieLunStatisticIncrementParam activityZhouJieLunStatisticIncrementParam = new ActivityZhouJieLunStatisticIncrementParam().setKey(ZhouJieLunStatisticConstant.NUMBER_OF_NEW_USERS);
            statisticIncrement(activityZhouJieLunStatisticIncrementParam);
            //活动排名添加
            redisTemplate.opsForZSet().incrementScore(getLeaderboardKey(), invitUserInfo.getPhone(), 1);
            //增加抽奖次数
            inrLotteryCount(param.getUserId());
        }
        return result;
    }

    //详情列表
    @Override
    public ActivityZhouJieLunLeaderboardDetailsVo leaderboardDetails(ActivityZhouJieLunLeaderboardDetailsParam param) {
        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        UserInfo userInfo = userInfoService.getById(param.getUserId());
        if (Objects.isNull(userInfo)) {
            throw new ServiceException("用户不存在");
        }
        //获取用户排名
        Long rank = zSetOperations.reverseRank(getLeaderboardKey(), userInfo.getPhone());
        if (Objects.nonNull(rank)) {
            ++rank;
        }
        //获取用户分享人数
        Long count = Convert.toLong(zSetOperations.score(getLeaderboardKey(), userInfo.getPhone()), 0L);
        //获取前20名排名
        Set<ZSetOperations.TypedTuple<String>> typedTuples = zSetOperations.reverseRangeWithScores(getLeaderboardKey(), 0, 19);
        //转换排行榜数据
        ArrayList<ActivityZhouJieLunLeaderboardDetailsVoInner> leaderboard = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(typedTuples)) {
            for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
                ActivityZhouJieLunLeaderboardDetailsVoInner voInner = new ActivityZhouJieLunLeaderboardDetailsVoInner();
                voInner
                        .setPhone(PhoneUtil.replace(typedTuple.getValue()))
                        .setCount(Convert.toLong(typedTuple.getScore(), 0L));
                leaderboard.add(voInner);
            }
        }
        //获取抽奖次数
        Integer remainingTimes = getLotteryCount(param.getUserId());
        //封装最后数据
        ActivityZhouJieLunLeaderboardDetailsVo vo = new ActivityZhouJieLunLeaderboardDetailsVo();
        vo.setRank(rank).setCount(count).setLeaderboard(leaderboard).setRemainingTimes(remainingTimes);
        return vo;
    }

    //发送验证码
    @Override
    public AjaxResponseBody<Boolean> sendCode(String phone) {
        //判断活动是否结束
        checkActivityEndDate();
        if (ArgumentsCheckUtil.isEmpty(phone)) {
            throw new ServiceException("手机号不能为空");
        }
        if (!TextValidator.MOBILE.boolCheck(phone)) {
            throw new ServiceException("手机号格式非法");
        }
        //判断用户是否存在
        checkUserExist(phone);
        try {
            smsService.sendSmsForSignup(phone);
            return AjaxResponseBody.success(true);
        } catch (Throwable e) {
            throw new ServiceException(e.getMessage());
        }

    }

    //万分比
    //门票概率
    private Integer probability1 = 2;
    //15元概率
    private Integer probability2 = 50;
    private Integer probability2T = probability1 + probability2;
    //10元概率
    private Integer probability3 = 500;
    private Integer probability3T = probability2T + probability3;
    //5元概率
    private Integer probability4 = 2000;
    private Integer probability4T = probability3T + probability4;

    //抽奖
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ActivityZhouJieLunLotteryVo lottery(ActivityZhouJieLunLotteryParam param) {
        //判断活动是否结束
        checkActivityEndDate();
        Integer userId = param.getUserId();
        UserInfo userInfo = userInfoService.getById(userId);
        if (Objects.isNull(userInfo)) {
            throw new ServiceException("非法操作");
        }
        return redisUtil.runWithLock(getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LOTTERY_ALLOW) + ":" + userId, () -> {
            HashOperations<String, String, Integer> hashOperations = redisTemplate.opsForHash();
            //获取可抽次数
            Integer remainingTimes = getLotteryCount(userId);
            ActivityZhouJieLunLotteryVo activityZhouJieLunLotteryVo = new ActivityZhouJieLunLotteryVo();
            activityZhouJieLunLotteryVo.setEnable(false);
            activityZhouJieLunLotteryVo.setPrizeType(0);
            if (remainingTimes > 0) {
                activityZhouJieLunLotteryVo.setEnable(true);
                activityZhouJieLunLotteryVo.setRemainingTimes(remainingTimes - 1);
                //增加已抽次数
                hashOperations.increment(getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LOTTERY_COUNT), String.valueOf(userId), 1);
                int randomInt = RandomUtil.randomInt(0, 10000);
                if (randomInt < probability1) {
                    if (sendTickets(userId, userInfo.getPhone())) {
                        activityZhouJieLunLotteryVo.setPrizeType(1);
                    }
                } else if (randomInt < probability2T) {
                    if (sendCoupons(userId, userInfo.getPhone(), 15)) {
                        activityZhouJieLunLotteryVo.setPrizeType(4);
                    }
                } else if (randomInt < probability3T) {
                    if (sendCoupons(userId, userInfo.getPhone(), 10)) {
                        activityZhouJieLunLotteryVo.setPrizeType(3);
                    }
                } else if (randomInt < probability4T) {
                    if (sendCoupons(userId, userInfo.getPhone(), 5)) {
                        activityZhouJieLunLotteryVo.setPrizeType(2);
                    }
                }
            }
            return activityZhouJieLunLotteryVo;
        });
    }

    //发门票
    private boolean sendTickets(Integer userId, String phone) {
        return redisUtil.runWithLock(getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LOTTERY_TICKETS + ":lock"), () -> {
            ValueOperations<String, Integer> valueOperations = redisTemplate.opsForValue();
            //获取可发的票
            Integer tickets = Convert.toInt(valueOperations.get(getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LOTTERY_TICKETS)), 0);
            if (tickets > 0) {
                //减少票数
                valueOperations.decrement(getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LOTTERY_TICKETS));
                //记录
                ActivityDrawRecord activityDrawRecord = new ActivityDrawRecord();
                activityDrawRecord.setUserId(userId);
                activityDrawRecord.setPhone(phone);
                activityDrawRecord.setResultContent("获得周杰伦演唱会门票一张");
                activityDrawRecord.setActivity("周杰伦");
                activityDrawRecord.setStatus(0);
                activityDrawRecordService.save(activityDrawRecord);
                log.info("周杰伦活动:获得周杰伦演唱会门票的用户ID为:{}", userId);
            }
            return true;
        });
    }

    //发券
    private boolean sendCoupons(Integer userId, String phone, Integer price) {
        return redisUtil.runWithLock(getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LOTTERY_COUPON + ":lock"), () -> {
            //获取可发的券
            CouponInfo couponInfo = couponInfoService.getOne(
                    new QueryWrapper<CouponInfo>()
                            .eq(CouponInfo.TYPE, 9)
                            .eq(CouponInfo.PRICE, price * 100)
                            .last("limit 1")
            );
            //判断是否有券
            if (Objects.isNull(couponInfo) || CompareUtil.compare(couponInfo.getRemainCount(), 0) <= 0) {
                return false;
            }
            //减少券的余量
            couponInfoService.update(
                    new UpdateWrapper<CouponInfo>()
                            .set(CouponInfo.GET_COUNT, couponInfo.getGetCount() + 1)
                            .set(CouponInfo.REMAIN_COUNT, couponInfo.getRemainCount() - 1)
                            .eq(CouponInfo.ID, couponInfo.getId())
            );
            //添加券
            UserCouponInfo userCouponInfo = new UserCouponInfo();
            Date date = new Date();
            userCouponInfo.setGmtCreate(date);
            userCouponInfo.setGmtModified(date);
            userCouponInfo.setOverTime(DateUtil.offsetDay(date, couponInfo.getDuration()));
            userCouponInfo.setUserId(userId);
            userCouponInfo.setCouponId(couponInfo.getId());
            userCouponInfo.setCouponValue(couponInfo.getPrice());
            userCouponInfo.setSource(3);
            userCouponInfo.setUsed(0);
            userCouponInfoService.save(userCouponInfo);
            //记录
            ActivityDrawRecord activityDrawRecord = new ActivityDrawRecord();
            activityDrawRecord.setUserId(userId);
            activityDrawRecord.setPhone(phone);
            activityDrawRecord.setResultContent("获得" + price + "元优惠券一张");
            activityDrawRecord.setActivity("周杰伦");
            activityDrawRecord.setStatus(1);
            activityDrawRecordService.save(activityDrawRecord);
            log.info("周杰伦活动:获得{}元优惠券的用户ID为:{}", price, userId);
            return true;
        });
    }

    //获取抽奖次数
    private Integer getLotteryCount(Integer userId) {
        HashOperations<String, String, Integer> hashOperations = redisTemplate.opsForHash();
        //获取今天已抽的次数
        Integer numberOfDraws = Convert.toInt(hashOperations.get(getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LOTTERY_COUNT), String.valueOf(userId)), 0);
        if (Objects.isNull(numberOfDraws)) {
            numberOfDraws = 0;
        }
        //获取今天额外的次数
        Integer extraTimes = Convert.toInt(hashOperations.get(getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LOTTERY_ALLOW), String.valueOf(userId)), 0);
        if (Objects.isNull(extraTimes)) {
            extraTimes = 0;
        }
        //剩余次数
        Integer remainingTimes = extraTimes + 1 - numberOfDraws;
        if (remainingTimes < 0) {
            remainingTimes = 0;
        }
        return remainingTimes;
    }

    //增加抽奖次数
    private void inrLotteryCount(Integer userId) {
        redisUtil.runWithLock(getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LOTTERY_ALLOW) + ":" + userId, () -> {
            HashOperations<String, String, Integer> hashOperations = redisTemplate.opsForHash();
            //获取今天额外的次数
            Integer extraTimes = Convert.toInt(hashOperations.get(getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LOTTERY_ALLOW), String.valueOf(userId)), 0);
            if (Objects.isNull(extraTimes)) {
                extraTimes = 0;
            }
            if (extraTimes < 4) {
                hashOperations.increment(getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LOTTERY_ALLOW), String.valueOf(userId), 1);
            }
            return null;
        });
    }

    /**
     * 获取redis中周杰伦活动数据
     *
     * @return
     */
    @Override
    public UserDataStat getRedisActivityData(String key) {
        try {
            Map<String, String> resultMap = redisTemplate.opsForHash().entries(key);
            UserDataStat userDataStat = new UserDataStat();
            userDataStat.setDate(DateUtil.beginOfDay(new Date()));
            if (null != resultMap.get(ZhouJieLunStatisticConstant.NUMBER_OF_ACTIVE_FORWARDERS)) {
                userDataStat.setActivityShareCount(Integer.valueOf(resultMap.get(ZhouJieLunStatisticConstant.NUMBER_OF_ACTIVE_FORWARDERS)));
            } else {
                userDataStat.setActivityShareCount(0);
            }
            if (null != resultMap.get(ZhouJieLunStatisticConstant.NUMBER_OF_NEW_USERS)) {
                userDataStat.setNewUserCount(Integer.valueOf(resultMap.get(ZhouJieLunStatisticConstant.NUMBER_OF_NEW_USERS)));
            } else {
                userDataStat.setNewUserCount(0);
            }
            if (null != resultMap.get(ZhouJieLunStatisticConstant.IN_SITE_PAGE_VISITS)) {
                userDataStat.setActivityInCount(Integer.valueOf(resultMap.get(ZhouJieLunStatisticConstant.IN_SITE_PAGE_VISITS)));
            } else {
                userDataStat.setActivityInCount(0);
            }
            if (null != resultMap.get(ZhouJieLunStatisticConstant.ACTIVE_PAGE_VISITS)) {
                userDataStat.setActivityVisitCount(Integer.valueOf(resultMap.get(ZhouJieLunStatisticConstant.ACTIVE_PAGE_VISITS)));
            } else {
                userDataStat.setActivityVisitCount(0);
            }
            userDataStat.setCreateTime(new Date());
            return userDataStat;
        } catch (Exception e) {
            e.printStackTrace();
            log.info("redis读取周杰伦活动数据异常");
        }
        return null;
    }

    private String getActivityKey(String endKey) {
        return RedisKeyPreConstant.STATISTICAL_DATA_PRE + RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY + endKey;
    }

    private String getLeaderboardKey() {
        return getActivityKey(RedisKeyPreConstant.STATISTICAL_DATA_MIDDLE_ACTIVITY_END_ZHOUJIELUN_LEADERBOARD);
    }

    //判断活动是否结束
    private void checkActivityEndDate() {
        if (DateUtil.compare(new Date(), zhouJieLunActivityEndTime) > 0) {
            throw new ServiceException("该活动已经结束");
        }
    }

    //判断用户是否存在
    private void checkUserExist(String phone) {
        //通过该手机号查询该用户是否存在
        UserInfo userInfo = userInfoService.selectUseInfoPhone(phone);
        if (Objects.nonNull(userInfo)) {
            log.info("该手机号已经存在{}", phone);
            throw new ServiceException("该活动仅限新用户参与");
        }
    }

    /**
     * 周杰伦活动用户邀请人数排名
     *
     * @return
     */
    @Override
    public List<UserDataStatInviteSortResult> inviteSort() {
        List<UserDataStatInviteSortResult> results = Lists.newArrayList();
        try {
            ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
            //获取前20名排名
            Set<ZSetOperations.TypedTuple<String>> typedTuples = zSetOperations.reverseRangeWithScores(getLeaderboardKey(), 0, 99);
            //转换排行榜数据
            for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
                UserDataStatInviteSortResult result = new UserDataStatInviteSortResult();
//                result.setPhone(StrUtil.replace(typedTuple.getValue(), 3, 7, '*'));
                result.setPhone(typedTuple.getValue());
                result.setTotalAmount(typedTuple.getScore().intValue());
                results.add(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return results;
    }

}
